import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Semaphore;

public class BoundedHashSet<T> {

    static public void main(String args[]) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "尝试进入");
                    semaphore.acquire();
                    long time = (long) (Math.random() * 10 + 1);
                    System.out.println(Thread.currentThread().getName() + "进入了" + time + "毫秒");
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "开始驶离");
                    // 释放
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + "离开了");
                }
            }, i + "号汽车").start();
        }
        BoundedHashSet<Integer> bhs = new BoundedHashSet<>(3);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Random random = new Random();
                    int num = random.nextInt(50);
                    bhs.add(num);
                    Thread.sleep(random.nextInt(1000));
                    bhs.remove(num);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            ;
        }
    }

    private final Set<T> set;
    private final Semaphore sem;

    public BoundedHashSet(int bound) {
        this.set = Collections.synchronizedSet(new HashSet<T>());
        sem = new Semaphore(bound);
    }

    public boolean add(T o) throws InterruptedException {
        sem.acquire();
        boolean wasAdded = false;
        try {
            wasAdded = set.add(o);
            System.out.println(Thread.currentThread().getName() + " add " + getTime());
            return wasAdded;
        } finally {
            if (!wasAdded) {
                sem.release();
            }
        }
    }

    public boolean remove(Object o) {
        boolean wasRemoved = set.remove(o);
        if (wasRemoved) {
            System.out.println(Thread.currentThread().getName() + " remove " + getTime());
            sem.release();
        }
        return wasRemoved;
    }

    private String getTime() {
        Date date = new Date();
        SimpleDateFormat df = new SimpleDateFormat(" yyyy-MM-dd HH:mm:ss");
        return df.format(date);
    }
}
